package com.ljp.chat.support.expansion.service;

import com.baomidou.mybatisplus.annotation.TableName;
import com.ljp.chat.exception.WarnException;
import com.ljp.chat.support.expansion.annotations.UniqueFiled;
import com.ljp.chat.support.expansion.exception.UniqueException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Component
public class UniqueCheck {

    /**
     * 定义操作类型
     */
    public static final String INSERT = "INSERT";
    public static final String UPDATE = "UPDATE";

    private static PreparedStatement ps = null;
    private static ResultSet rs = null;

    private static Connection con = null;

    private final DataSource dataSource;

    public UniqueCheck(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @PostConstruct
    public void getConnection() throws SQLException {
        con = dataSource.getConnection();
    }

    public static <T> void uniqueCheck(T t, String type, String logicDeleteFiled) throws Exception {
        //获得目标对象的类对象
        Class<?> objClass = t.getClass();

        TableName tableName = objClass.getAnnotation(TableName.class);

        //如果目标实体类注解中不包含TableName注解则报错
        Optional.ofNullable(tableName).orElseThrow(() -> new WarnException("When using the unique verification function, the TableName annotation is empty"));

        //获得改类及其父类的所有带有UniqueFiled注解的字段（不包括id字段）
        List<Field> fields = getAllFieldByObjClass(objClass);

        //根据UniqueFiled.group进行分组
        Map<String, List<Field>> fieldMap = fields.stream().collect(Collectors.groupingBy(field -> field.getAnnotation(UniqueFiled.class).group()));

        //将Filed集合转换为sql语句
        Function<List<Field>, String> getSqlByFields = list -> {
            boolean logicDeleteFlag = StringUtils.isNoneBlank(logicDeleteFiled);
            boolean update = UPDATE.equals(type);
            boolean firstJointFlag = false;

            StringBuilder builder = new StringBuilder("select count(*) from " + tableName.value() + " where ");

            builder.append(logicDeleteFlag ? logicDeleteFiled + " = false " : "");

            for (Field field : list) {
                UniqueFiled annotation = field.getAnnotation(UniqueFiled.class);
                if (StringUtils.isNoneBlank(logicDeleteFiled) || firstJointFlag) {
                    builder.append(" and ").append(annotation.filed()).append(" = ? ");
                } else {
                    builder.append(annotation.filed()).append(" = ? ");
                }
                firstJointFlag = true;
            }

            //SQL拼接分为两种情况 1、更新且逻辑删除  2、更新没有逻辑删除
            if (update && logicDeleteFlag) {
                //情况1
                builder.append(" and id != ?;");
            } else if (update) {
                builder.append(" id != ?; ");
            }

            return builder.toString();
        };


        //每组字段进行不同的校验
        for (Map.Entry<String, List<Field>> entry : fieldMap.entrySet()) {
            //SQL拼接
            String sql = getSqlByFields.apply(entry.getValue());
            log.info("Unique sql:{}", sql);

            //获得值集合values
            List<Object> values = getValuesByField(objClass, t, entry.getValue(), type);

            //执行SQl
            ps = con.prepareStatement(sql);
            parameterAssignment(ps, values);
            rs = ps.executeQuery();

            StringBuilder builder = new StringBuilder();
            List<String> list = entry.getValue().stream().map(filed -> filed.getAnnotation(UniqueFiled.class).message()).collect(Collectors.toList());
            list.forEach(item -> builder.append(item).append(","));

            //获得结果集
            while (rs.next()) {
                if (rs.getInt(1) > 0) {
                    throw new UniqueException(builder.deleteCharAt(builder.length() - 1).toString() + "已存在");
                }
            }
        }

        //资源关闭
        if (null != rs)
            rs.close();
        if (null != ps)
            ps.close();
    }

    /**
     * Get all field by obj class list.
     * 获得目标对象的范围字段 （范围：带有UniqueFiled注解的字段）
     *
     * @param obj the obj
     * @return the list
     */
    private static List<Field> getAllFieldByObjClass(Class<?> obj) {
        //获得目标对象类中所有带有UniqueFiled注解的字段
        List<Field> fields = Arrays.stream(obj.getDeclaredFields()).filter(item -> null != item.getAnnotation(UniqueFiled.class)).collect(Collectors.toList());

        //递归获取所有字段
        if (null != obj.getSuperclass()) {
            fields.addAll(getAllFieldByObjClass(obj.getSuperclass()));
        }
        return fields;
    }

    /**
     * Parameter assignment.
     * SQL参数赋值
     *
     * @param ps         the ps
     * @param parameters the parameters
     */
    private static void parameterAssignment(PreparedStatement ps, List<Object> parameters) {
        try {
            for (int i = 1; i <= parameters.size(); i++) {
                ps.setObject(i, parameters.get(i - 1));
            }
        } catch (SQLException e) {
            log.error("Error in parameter assignment when using the unique verification function");
            e.printStackTrace();
        }
    }

    /**
     * Gets values by field.
     *
     * @param <T>      the type parameter
     * @param objClass the obj class
     * @param t        the t
     * @param fields   the fields
     * @param type     the type
     * @return the values by field
     * @throws Exception the exception
     */
    private static <T> List<Object> getValuesByField(Class<?> objClass, T t, List<Field> fields, String type) throws Exception {
        //获得值集合values
        List<Object> values = new ArrayList<>();
        for (Field field : fields) {
            field.setAccessible(true);
            values.add(field.get(t));
        }

        //如果是更新操作把id的值添加到values集合中
        if (UPDATE.equals(type)) {
            Field idFiled = getIdFiled(objClass);
            idFiled.setAccessible(true);
            values.add(idFiled.get(t));
        }
        return values;
    }

    /**
     * Gets id filed.
     * 获得id字段
     *
     * @param objClass the obj class
     * @return the id filed
     */
    private static Field getIdFiled(Class<?> objClass) {
        Field field;
        try {
            field = objClass.getDeclaredField("id");
        } catch (NoSuchFieldException e) {
            field = getIdFiled(objClass.getSuperclass());
        }
        return field;
    }

}
